package jp.sourceforge.talisman.mds.cluster;

/*
 * $Id: Cluster.java,v 1.1 2009/09/12 05:06:00 weiwei Exp $
 */

import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

import jp.sourceforge.talisman.mds.Item;

/**
 * 
 * @author Haruaki Tamada
 * @version $Revision: 1.1 $ 
 */
public class Cluster implements Serializable, Iterable<Item> {
	private static final long serialVersionUID = -8171194255774476986L;

	private String name;
	private Set<Item> itemSet = new LinkedHashSet<Item>();
	private Item median;

	/**
	 * default constructor.
	 */
	public Cluster(){
	}

	/**
	 * self constructor.
	 */
	public Cluster(Cluster cluster){
	    if(cluster.getName() != null){
	        setName(cluster.getName());
	    }
	    for(Item item: cluster){
	        addItem(item);
	    }
	}

	public synchronized void addItem(Item item) {
        itemSet.add(item);
        median = null;
	}

	public synchronized Item getItem(int targetIndex){
	    int index = 0;
	    Item targetItem = null;
	    for(Item item: this){
	        if(index == targetIndex){
	            targetItem = item;
	            break;
	        }
	        index++;
	    }
	    return targetItem;
	}

	public synchronized boolean removeItem(Item item) {
        median = null;
		return itemSet.remove(item);
	}

	public synchronized Item remove(int removeIndex){
	    median = null;

	    Set<Item> newItemSet = new LinkedHashSet<Item>();
	    int index = 0;
	    Item removedItem = null;
	    for(Item item: this){
	        if(index == removeIndex){
	            removedItem = item;
	        }
	        else{
	            newItemSet.add(item);
	        }
	        index++;
	    }
	    this.itemSet = newItemSet;
	    return removedItem;
	}

    public Iterator<Item> iterator(){
        return itemSet.iterator();
    }

	public int getSize(){
	    return itemSet.size();
	}

	public boolean isContain(Item item){
	    return itemSet.contains(item);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public synchronized Item getMedian(){
        if(median == null){
	        median = calculateMedian();
	    }
	    return median;
    }

    public synchronized Item[] getItems(){
        return itemSet.toArray(new Item[getSize()]);
    }

    public synchronized String toString(){
        StringBuilder sb = new StringBuilder();
        if(getName() != null) sb.append(getName()).append(" ");
        sb.append("{");
        int index = 0;
        for(Item item: this){
            if(index != 0) sb.append(", ");
            sb.append(item);
            index++;
        }
        sb.append(" }");
        return new String(sb);
    }

	private Item calculateMedian(){
		if(itemSet.size() == 1){
            return new Item(itemSet.toArray(new Item[1])[0]);
		}
		else if(itemSet.size() == 0){
			return null;
		}
		else{
		    Item[] items = itemSet.toArray(new Item[itemSet.size()]);
		    int dim = items[0].getDimension();
			double[] median = new double[dim];
			for(Item item: items){
				for(int i = 0; i < dim; i++){
					median[i] += item.get(i).doubleValue();
				}
			}
			for(int i = 0; i < dim; i++){
				median[i] = median[i] / items.length;
			}
			return new Item("<median>", median);
		}
	}
}
